package com.rhaggarty.blackjack.cardroom;

import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicReference;

import com.rhaggarty.blackjack.game.GameStatus;
import com.rhaggarty.blackjack.game.Hand;
import com.rhaggarty.blackjack.game.Player;
import com.rhaggarty.data.type.game.Card;

/**
 * A representation of a seat at a blackjack table. This class also contains a nested {@link #UnmodifiableSeat} class that
 * should be used for static seats like dealers.
 * 
 * @author Ryan Haggarty (ryanmh@gmail.com)
 */
public class Seat {

    private static final BigDecimal MAX_STACK = new BigDecimal(Long.MAX_VALUE);

    private BigDecimal amountInPlay = BigDecimal.ZERO;

    private Hand hand;

    private AtomicReference<Player> player = new AtomicReference<Player>();

    private GameStatus status = GameStatus.PENDING;

    public static Seat newDealerSeat() {
        return new UnmodifiableSeat(new Player(MAX_STACK));
    }

    public static Seat emptySeat() {
        return new Seat();
    }

    private Seat() {
        player.set(null);
        clearCards();
    }

    public void addWager(final BigDecimal amount) {
        // Order is important here, as attempting to alter a player's stack may result
        // in an Exception on insufficient funds...
        player.get().alterStack(amount);
        amountInPlay = amountInPlay.add(amount);
    }

    public void clearCards() {
        hand = null;
        amountInPlay = BigDecimal.ZERO;
    }

    public void dealCard(final Card card) {
        if (hand == null) {
            hand = new Hand(card);
        } else {
            hand.addCard(card);
        }
    }

    public void removePlayer() {
        this.player.set(null);
        clearCards();
    }

    public void sitPlayer(final Player player) {
        if (!this.player.compareAndSet(null, player)) {
            throw new IllegalStateException("Seat is already occupied");
        }
    }

    public BigDecimal getAmountInPlay() {
        return amountInPlay;
    }

    public Hand getHand() {
        return hand;
    }

    public Player getPlayer() {
        return player.get();
    }

    public GameStatus getStatus() {
        return status;
    }

    public void setStatus(final GameStatus status) {
        this.status = status;
    }

    private static final class UnmodifiableSeat extends Seat {

        private UnmodifiableSeat(final Player player) {
            super.player.set(player);
        }

        @Override
        public void removePlayer() {
            throw new UnsupportedOperationException("Cannot modify an unmodifiable seat");
        }

        @Override
        public void sitPlayer(final Player player) {
            throw new UnsupportedOperationException("Cannot modify an unmodifiable seat");
        }
    }
}
